package com.gxc.service.common.components.log.aspect;

import com.gxc.service.common.components.log.model.GlobalAccessLog;
import com.gxc.service.common.components.log.model.LogManager;
import com.gxc.service.common.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
 * 请求日志切面.
 *
 * @author GongXincheng
 * @since 2019-11-08 23:48
 */
@Slf4j
@Aspect
public class LogAspect {

    @Pointcut(value = "execution(* com.gxc.service..controller..*(..))")
    public void pointCut() {
    }

    @Around(value = "pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.warn("{}#around() -> into log Aspect method", this.getClass().getSimpleName());
        // 获取方法和请求入参
        getMethodInfo(joinPoint);
        return joinPoint.proceed();
    }

    /**
     * 保存获取方法和请求入参到ThreadLocal中
     */
    private void getMethodInfo(ProceedingJoinPoint joinPoint) {
        // 获取Controller的类名
        GlobalAccessLog logInfo = LogManager.getLogInfo();
        String controllerClassName = joinPoint.getTarget().getClass().getName();
        logInfo.setClassName(controllerClassName);

        // 获取方法名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        logInfo.setMethodName(method.getName());

        // 获取请求参数的信息.
        String[] parameterNames = signature.getParameterNames();
        Object[] args = joinPoint.getArgs();
        Map<String, Object> paramMap = buildRequestParam(parameterNames, args);
        logInfo.setMethodParams(JsonUtil.toJsonString(paramMap));
    }

    /**
     * 获取请求参数.
     *
     * @param parameterNames 方法参数的名称数组
     * @param args           参数值数组
     * @return Map
     */
    private Map<String, Object> buildRequestParam(String[] parameterNames, Object[] args) {
        Map<String, Object> result = new HashMap<>(16);
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            if (arg instanceof ServletRequest
                    || arg instanceof HttpSession
                    || arg instanceof ServletResponse
                    || arg instanceof MultipartFile) {
                continue;
            }
            result.put(parameterNames[i], arg);
        }
        return result;
    }
}
